// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:io';

import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";

void testZLibDeflateEmpty() {
  asyncStart();
  var controller = new StreamController(sync: true);
  controller.stream.transform(new ZLibEncoder(gzip: false, level: 6))
      .fold([], (buffer, data) {
        buffer.addAll(data);
        return buffer;
      })
      .then((data) {
        Expect.listEquals([120, 156, 3, 0, 0, 0, 0, 1], data);
        asyncEnd();
      });
  controller.close();
}

void testZLibDeflateEmptyGzip() {
  asyncStart();
  var controller = new StreamController(sync: true);
  controller.stream.transform(new ZLibEncoder(gzip: true, level: 6))
      .fold([], (buffer, data) {
        buffer.addAll(data);
        return buffer;
      })
      .then((data) {
        Expect.isTrue(data.length > 0);
        Expect.listEquals([], new ZLibDecoder().convert(data));
        asyncEnd();
      });
  controller.close();
}

void testZLibDeflate() {
  asyncStart();
  var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  var controller = new StreamController(sync: true);
  controller.stream.transform(new ZLibEncoder(gzip: false, level: 6))
      .fold([], (buffer, data) {
        buffer.addAll(data);
        return buffer;
      })
      .then((data) {
        Expect.listEquals(
            [120, 156, 99, 96, 100, 98, 102, 97, 101, 99, 231, 224, 4, 0, 0,
            175, 0, 46],
            data);
        asyncEnd();
      });
  controller.add(data);
  controller.close();
}

void testZLibDeflateGZip() {
  asyncStart();
  var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  var controller = new StreamController(sync: true);
  controller.stream.transform(new ZLibEncoder(gzip: true))
      .fold([], (buffer, data) {
        buffer.addAll(data);
        return buffer;
      })
      .then((data) {
        Expect.equals(30, data.length);
        Expect.listEquals([99, 96, 100, 98, 102, 97, 101, 99, 231, 224, 4, 0,
                           70, 215, 108, 69, 10, 0, 0, 0],
                          // Skip header, as it can change.
                          data.sublist(10));
        asyncEnd();
      });
  controller.add(data);
  controller.close();
}

void testZLibDeflateRaw() {
  asyncStart();
  var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  var controller = new StreamController(sync: true);
  controller.stream.transform(new ZLibEncoder(raw: true, level: 6))
      .fold([], (buffer, data) {
        buffer.addAll(data);
        return buffer;
      })
      .then((data) {
        Expect.listEquals([99, 96, 100, 98, 102, 97, 101, 99, 231, 224, 4, 0],
            data);
        asyncEnd();
      });
  controller.add(data);
  controller.close();
}

void testZLibDeflateInvalidLevel() {
  test2(gzip, level) {
  [true, false].forEach((gzip) {
    [-2, -20, 10, 42, null, "9"].forEach((level) {
      Expect.throws(
              () => new ZLibEncoder(gzip: gzip, level: level),
              (e) => e is ArgumentError,
          "'level' must be in range -1..9"
      );
      });
    });
  };
}

void testZLibInflate() {
  var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  [true, false].forEach((gzip) {
    [ZLibOption.STRATEGY_FILTERED, ZLibOption.STRATEGY_HUFFMAN_ONLY,
        ZLibOption.STRATEGY_RLE, ZLibOption.STRATEGY_FIXED,
        ZLibOption.STRATEGY_DEFAULT].forEach((strategy) {
      [3, 6, 9].forEach((level) {
        asyncStart();
        var controller = new StreamController(sync: true);
        controller.stream
            .transform(new ZLibEncoder(gzip: gzip, level: level,
                                       strategy: strategy))
            .transform(new ZLibDecoder())
            .fold([], (buffer, data) {
              buffer.addAll(data);
              return buffer;
            })
            .then((inflated) {
              Expect.listEquals(data, inflated);
              asyncEnd();
            });
        controller.add(data);
        controller.close();
      });
    });
  });
}

void testZLibInflateRaw() {
  var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  [3, 6, 9].forEach((level) {
    asyncStart();
    var controller = new StreamController(sync: true);
    controller.stream
        .transform(new ZLibEncoder(raw: true, level: level))
        .transform(new ZLibDecoder(raw: true))
        .fold([], (buffer, data) {
          buffer.addAll(data);
          return buffer;
        })
        .then((inflated) {
          Expect.listEquals(data, inflated);
          asyncEnd();
        });
    controller.add(data);
    controller.close();
  });
}

void testZLibInflateSync() {
  var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  [true, false].forEach((gzip) {
    [3, 6, 9].forEach((level) {
      var encoded = new ZLibEncoder(gzip: gzip, level: level).convert(data);
      var decoded = new ZLibDecoder().convert(encoded);
      Expect.listEquals(data, decoded);
    });
  });
}

void testZlibInflateThrowsWithSmallerWindow() {
  var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  var encoder = new ZLibEncoder(windowBits: 10);
  var encodedData = encoder.convert(data);
  var decoder = new ZLibDecoder(windowBits: 8);
  Expect.throws(() => decoder.convert(encodedData));
}

void testZlibInflateWithLargerWindow() {
  var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  [true, false].forEach((gzip) {
    [3, 6, 9].forEach((level) {
      asyncStart();
      var controller = new StreamController(sync: true);
      controller.stream
          .transform(new ZLibEncoder(gzip: gzip, level: level, windowBits: 8))
          .transform(new ZLibDecoder(windowBits: 10))
          .fold([], (buffer, data) {
            buffer.addAll(data);
            return buffer;
          })
          .then((inflated) {
            Expect.listEquals(data, inflated);
            asyncEnd();
          });
      controller.add(data);
      controller.close();
    });
  });
}

void testZlibWithDictionary() {
  var dict = [102, 111, 111, 98, 97, 114];
  var data = [98, 97, 114, 102, 111, 111];

  [3, 6, 9].forEach((level) {
    var encoded = new ZLibEncoder(level: level, dictionary: dict)
        .convert(data);
    var decoded = new ZLibDecoder(dictionary: dict).convert(encoded);
    Expect.listEquals(data, decoded);
  });
}

void main() {
  asyncStart();
  testZLibDeflate();
  testZLibDeflateEmpty();
  testZLibDeflateEmptyGzip();
  testZLibDeflateGZip();
  testZLibDeflateInvalidLevel();
  testZLibInflate();
  testZLibInflateSync();
  testZlibInflateThrowsWithSmallerWindow();
  testZlibInflateWithLargerWindow();
  testZLibDeflateRaw();
  testZLibInflateRaw();
  testZlibWithDictionary();
  asyncEnd();
}
